1. Introduzione

Questo documento presenta un’analisi del mercato immobiliare italiano, con particolare attenzione ai fattori che influenzano i prezzi degli immobili. Verranno esaminati diversi modelli statistici per comprendere la relazione tra le caratteristiche degli immobili e il loro prezzo. Il dataset è composto da poco più di 51000 osservazioni

2. Caricamento Librerie e Dati

# Pulizia dell'ambiente di lavoro
rm(list = ls())

# Caricamento librerie necessarie
library(ISLR2)       # Dataset e funzioni per "Introduction to Statistical Learning"
library(leaps)       # Per la selezione di sottoinsiemi di variabili
library(ggplot2)     # Per grafici avanzati
library(regclass)    # Per funzioni di regressione
library(corrplot)    # Per matrici di correlazione
library(sf)          # Per dati spaziali
library(dplyr)       # Per manipolazione dati
library(tmap)        # Per mappe tematiche
library(MASS)        # Per funzioni statistiche (LDA, QDA, ecc.)
library(car)         # Per diagnostica di regressione
library(caret)       # Per matrice di confusione e valutazione modelli
library(ROCR)        # Per curve ROC
# Caricamento dati
data <- read.csv("data.csv")

# Riorganizzazione delle colonne: prezzo come prima colonna
data <- data %>% relocate(prezzo)

# Visualizzazione primi record
head(data)
##   prezzo             regione  citta posti.auto bagni stanze ultimo.piano
## 1    650 trentino-alto-adige  Lavis          1     1      2            0
## 2   1000               lazio   Roma          1     1      3            0
## 3    850           lombardia Milano          0     1      1            0
## 4    720           lombardia Milano          0     1      2            0
## 5    900             liguria Genova          1     1      3            0
## 6    800           lombardia Milano          0     1      1            1
##                    stato classe.energetica vista.mare
## 1 ottimo / ristrutturato                 A          0
## 2      buono / abitabile                 G          0
## 3 ottimo / ristrutturato                 A          0
## 4 ottimo / ristrutturato                 B          0
## 5      buono / abitabile                 G          0
## 6      buono / abitabile                 E          0
##   riscaldamento.centralizzato superficie arredato balcone esposizione.esterna
## 1                           0         50        1       0                   1
## 2                           0        100        0       0                   0
## 3                           0         30        1       0                   0
## 4                           1         45        1       1                   1
## 5                           1         85        0       1                   0
## 6                           1         45        1       0                   0
##   fibra.ottica cancello.elettrico cantina giardino.comune giardino.privato
## 1            1                  0       0               0                0
## 2            1                  0       0               0                0
## 3            0                  0       0               0                0
## 4            1                  1       1               0                0
## 5            0                  0       0               0                0
## 6            0                  0       0               0                0
##   piscina villa appartamento attico loft mansarda
## 1       0     0            1      0    0        0
## 2       0     0            1      0    0        0
## 3       0     0            1      0    0        0
## 4       0     0            1      0    0        0
## 5       0     0            1      0    0        0
## 6       0     0            1      0    0        0
# Riepilogo statistico dei dati
summary(data)
##      prezzo         regione             citta             posti.auto    
##  Min.   : 170.0   Length:51752       Length:51752       Min.   :0.0000  
##  1st Qu.: 550.0   Class :character   Class :character   1st Qu.:0.0000  
##  Median : 730.0   Mode  :character   Mode  :character   Median :0.0000  
##  Mean   : 813.5                                         Mean   :0.3591  
##  3rd Qu.:1000.0                                         3rd Qu.:1.0000  
##  Max.   :2830.0                                         Max.   :1.0000  
##      bagni           stanze      ultimo.piano       stato          
##  Min.   :1.000   Min.   :1.00   Min.   :0.0000   Length:51752      
##  1st Qu.:1.000   1st Qu.:2.00   1st Qu.:0.0000   Class :character  
##  Median :1.000   Median :2.00   Median :0.0000   Mode  :character  
##  Mean   :1.192   Mean   :2.58   Mean   :0.1524                     
##  3rd Qu.:1.000   3rd Qu.:3.00   3rd Qu.:0.0000                     
##  Max.   :5.000   Max.   :5.00   Max.   :1.0000                     
##  classe.energetica    vista.mare      riscaldamento.centralizzato
##  Length:51752       Min.   :0.00000   Min.   :0.0000             
##  Class :character   1st Qu.:0.00000   1st Qu.:0.0000             
##  Mode  :character   Median :0.00000   Median :0.0000             
##                     Mean   :0.01256   Mean   :0.2734             
##                     3rd Qu.:0.00000   3rd Qu.:1.0000             
##                     Max.   :1.00000   Max.   :1.0000             
##    superficie        arredato         balcone      esposizione.esterna
##  Min.   : 29.00   Min.   :0.0000   Min.   :0.000   Min.   :0.0000     
##  1st Qu.: 50.00   1st Qu.:1.0000   1st Qu.:0.000   1st Qu.:0.0000     
##  Median : 65.00   Median :1.0000   Median :1.000   Median :0.0000     
##  Mean   : 71.84   Mean   :0.7519   Mean   :0.541   Mean   :0.3846     
##  3rd Qu.: 86.00   3rd Qu.:1.0000   3rd Qu.:1.000   3rd Qu.:1.0000     
##  Max.   :300.00   Max.   :1.0000   Max.   :1.000   Max.   :1.0000     
##   fibra.ottica    cancello.elettrico    cantina       giardino.comune 
##  Min.   :0.0000   Min.   :0.0000     Min.   :0.0000   Min.   :0.0000  
##  1st Qu.:0.0000   1st Qu.:0.0000     1st Qu.:0.0000   1st Qu.:0.0000  
##  Median :0.0000   Median :0.0000     Median :0.0000   Median :0.0000  
##  Mean   :0.4412   Mean   :0.3214     Mean   :0.2148   Mean   :0.2024  
##  3rd Qu.:1.0000   3rd Qu.:1.0000     3rd Qu.:0.0000   3rd Qu.:0.0000  
##  Max.   :1.0000   Max.   :1.0000     Max.   :1.0000   Max.   :1.0000  
##  giardino.privato     piscina            villa          appartamento   
##  Min.   :0.00000   Min.   :0.00000   Min.   :0.00000   Min.   :0.0000  
##  1st Qu.:0.00000   1st Qu.:0.00000   1st Qu.:0.00000   1st Qu.:1.0000  
##  Median :0.00000   Median :0.00000   Median :0.00000   Median :1.0000  
##  Mean   :0.08042   Mean   :0.00912   Mean   :0.01992   Mean   :0.9454  
##  3rd Qu.:0.00000   3rd Qu.:0.00000   3rd Qu.:0.00000   3rd Qu.:1.0000  
##  Max.   :1.00000   Max.   :1.00000   Max.   :1.00000   Max.   :1.0000  
##      attico             loft             mansarda       
##  Min.   :0.00000   Min.   :0.000000   Min.   :0.000000  
##  1st Qu.:0.00000   1st Qu.:0.000000   1st Qu.:0.000000  
##  Median :0.00000   Median :0.000000   Median :0.000000  
##  Mean   :0.01291   Mean   :0.003188   Mean   :0.009893  
##  3rd Qu.:0.00000   3rd Qu.:0.000000   3rd Qu.:0.000000  
##  Max.   :1.00000   Max.   :1.000000   Max.   :1.000000

3. Pulizia e Trasformazione dei Dati

3.1. Aggregazione delle Regioni per Area Geografica

# Definizione delle aree geografiche
nord <- c('valle-d-aosta', 'piemonte', 'liguria', 'lombardia', 'trentino-alto-adige', 
          'veneto', 'friuli-venezia-giulia', 'emilia-romagna')
centro <- c('toscana', 'marche', 'umbria', 'lazio')
sud <- c('abruzzo', 'molise', 'campania', 'puglia', 'basilicata', 'calabria')
isole <- c('sardegna', 'sicilia')

# Riclassificazione delle regioni in aree geografiche
data <- data %>%
  mutate(regione = case_when(
    regione %in% nord ~ 'nord',
    regione %in% centro ~ 'centro',
    regione %in% sud ~ 'sud',
    regione %in% isole ~ 'isole'
  ))

3.2. Conversione dei Fattori Ordinati

# Conversione delle variabili categoriche in fattori
data$classe.energetica <- factor(data$classe.energetica, 
                                ordered = TRUE, 
                                levels = c("G", "F", "E", "D", "C", "B", "A"))

data$stato <- factor(data$stato, 
                    ordered = TRUE, 
                    levels = c("da ristrutturare", "buono / abitabile", 
                               "ottimo / ristrutturato", "nuovo / in costruzione"))

data$regione <- factor(data$regione, 
                      ordered = FALSE, 
                      levels = c("nord", "centro", "sud", "isole"))

# Controllo delle variabili fattoriali
str(data[, c("classe.energetica", "stato", "regione")])
## 'data.frame':    51752 obs. of  3 variables:
##  $ classe.energetica: Ord.factor w/ 7 levels "G"<"F"<"E"<"D"<..: 7 1 7 6 1 3 7 6 1 1 ...
##  $ stato            : Ord.factor w/ 4 levels "da ristrutturare"<..: 3 2 3 3 2 2 3 3 3 2 ...
##  $ regione          : Factor w/ 4 levels "nord","centro",..: 1 2 1 1 1 1 1 2 1 3 ...

4. Analisi Esplorativa dei Dati

4.1. Mappa dei Prezzi Medi per Provincia

# Caricamento dati per città
dataCitta <- read.csv("dataCitta.csv", sep = ';')

# Creazione della mappa con ggplot2
ggplot() +
  geom_polygon(data = map_data("italy"), 
               aes(x = long, y = lat, group = group), 
               fill = "white", colour = "black") +
  geom_point(data = dataCitta, 
             aes(x = long, y = lat, color = prezzoMedio, size = prezzoMedio),
             alpha = 0.7) + 
  scale_color_viridis_c(option = "plasma") +  # Migliore scala di colori
  coord_map() +
  labs(title = "Prezzo Medio degli Immobili per Provincia",
       x = "Longitudine", 
       y = "Latitudine",
       color = "Prezzo (€)",
       size = "Prezzo (€)") +
  theme_minimal() +
  theme(
    plot.title = element_text(size = 16, face = "bold"),
    legend.position = "right"
  )

4.2. Distribuzioni delle Variabili Chiave

# Distribuzione del prezzo
p1 <- ggplot(data = data, aes(x = prezzo)) +
  geom_histogram(fill = "#69b3a2", color = "white", alpha = 0.7, bins = 30) +
  labs(title = "Distribuzione dei Prezzi",
       x = "Prezzo (€)",
       y = "Frequenza") +
  theme_minimal()

# Distribuzione della superficie
p2 <- ggplot(data = data, aes(x = superficie)) +
  geom_histogram(fill = "#404080", color = "white", alpha = 0.7, bins = 30) +
  labs(title = "Distribuzione delle Superfici",
       x = "Superficie (m²)",
       y = "Frequenza") +
  theme_minimal()

# Visualizzazione affiancata
gridExtra::grid.arrange(p1, p2, ncol = 2)

4.3. Relazione tra Variabili Categoriche e Prezzo

# Funzione per creare boxplot con le variabili categoriche
create_boxplot <- function(var) {
  ggplot(data, aes_string(x = var, y = "prezzo")) +
    geom_boxplot(fill = "#69b3a2", alpha = 0.7) +
    labs(title = paste("Prezzo per", gsub("\\.", " ", var)),
         x = gsub("\\.", " ", var),
         y = "Prezzo (€)") +
    theme_minimal() +
    theme(axis.text.x = element_text(angle = 45, hjust = 1))
}

# Identificazione delle variabili categoriche
cat_vars <- names(data)[sapply(data, is.factor)]

# Creazione di boxplot per ogni variabile categorica
boxplots <- lapply(cat_vars, create_boxplot)

# Visualizzazione
do.call(gridExtra::grid.arrange, c(boxplots, ncol = 2))

5. Modelli di Regressione Lineare

5.1. Modello Base: Prezzo ~ Superficie

# Modello di regressione lineare semplice
model_base <- lm(prezzo ~ superficie, data = data)
summary(model_base)
## 
## Call:
## lm(formula = prezzo ~ superficie, data = data)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1002.88  -245.40   -72.91   185.00  1782.92 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 496.25856    3.81253  130.16   <2e-16 ***
## superficie    4.41637    0.04893   90.27   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 335.9 on 51750 degrees of freedom
## Multiple R-squared:  0.136,  Adjusted R-squared:  0.136 
## F-statistic:  8148 on 1 and 51750 DF,  p-value: < 2.2e-16

Commento dei risultati:

La regressione lineare semplice mostra una relazione positiva statisticamente significativa tra la superficie e il prezzo (p < 2e-16). Per ogni metro quadrato aggiuntivo, si stima che il prezzo aumenti di €4,42. Tuttavia, la sola superficie spiega solo circa il 13,6% della variazione del prezzo (R-quadro = 0,136), il che indica che altri fattori influenzano in modo significativo il prezzo degli immobili.

# Grafico della relazione
ggplot(data, aes(x = superficie, y = prezzo)) +
  geom_point(alpha = 0.5) +
  geom_smooth(method = "lm", color = "red") +
  labs(title = "Relazione tra Superficie e Prezzo",
       x = "Superficie (m²)",
       y = "Prezzo (€)") +
  theme_minimal()

# Grafici diagnostici per il modello
par(mfrow = c(2, 2))
plot(model_base, which = 1:4)

Il grafico dei residui rispetto ai valori predetti mostra una possibile non linearità o eteroscedasticità. Il Q-Q Plot evidenzia deviazioni dalla normalità, in particolare nelle code della distribuzione. Il grafico Scale-Location suggerisce anch’esso la presenza di eteroscedasticità. Il grafico della Cook’s distance mette in evidenza alcuni punti potenzialmente influenti.

5.2. Modello con Effetti di Interazione: Prezzo ~ Superficie * Regione

# Modello con interazione tra superficie e regione
model_interaction <- lm(prezzo ~ superficie * regione, data = data)
summary(model_interaction)
## 
## Call:
## lm(formula = prezzo ~ superficie * regione, data = data)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1207.45  -237.67   -70.76   181.72  1919.69 
## 
## Coefficients:
##                            Estimate Std. Error t value Pr(>|t|)    
## (Intercept)               519.01123    4.85048 107.002  < 2e-16 ***
## superficie                  4.37318    0.06425  68.069  < 2e-16 ***
## regionecentro            -117.08840    8.80247 -13.302  < 2e-16 ***
## regionesud               -161.27961   14.07487 -11.459  < 2e-16 ***
## regioneisole             -115.14199   22.39388  -5.142 2.73e-07 ***
## superficie:regionecentro    1.65444    0.11609  14.252  < 2e-16 ***
## superficie:regionesud      -0.03355    0.15637  -0.215  0.83013    
## superficie:regioneisole    -0.84437    0.23615  -3.576  0.00035 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 330.4 on 51705 degrees of freedom
##   (39 osservazioni eliminate a causa di valori mancanti)
## Multiple R-squared:  0.1641, Adjusted R-squared:  0.164 
## F-statistic:  1450 on 7 and 51705 DF,  p-value: < 2.2e-16

Commento dei risultati:

Questo modello analizza come la relazione tra prezzo e superficie vari tra le diverse regioni. I termini di interazione significativi (ad esempio, superficie:regionecentro) indicano che l’effetto della superficie sul prezzo è effettivamente diverso per ciascuna regione rispetto alla regione di riferimento (nord). Il valore di R² aggiustato (0,1632) è leggermente più alto rispetto al modello base, suggerendo che includere l’interazione con la regione migliora la capacità del modello di spiegare la variabilità dei prezzi.

# Creazione di un data frame per le linee di previsione
pred_data <- expand.grid(
  superficie = seq(min(data$superficie), max(data$superficie), length.out = 100),
  regione = levels(data$regione)
)
pred_data$prezzo <- predict(model_interaction, newdata = pred_data)

# Grafico con linee di regressione per regione
ggplot() +
  geom_point(data = data, aes(x = superficie, y = prezzo, color = regione), alpha = 0.5) +
  geom_line(data = pred_data, aes(x = superficie, y = prezzo, color = regione), size = 1) +
  scale_color_brewer(palette = "Set1") +
  labs(title = "Relazione tra Superficie e Prezzo per Regione",
       x = "Superficie (m²)",
       y = "Prezzo (€)",
       color = "Regione") +
  theme_minimal()

5.3. Modello Completo: Prezzo ~ Tutte le Variabili

# Rimozione della variabile città che potrebbe causare problemi di multicollinearità
data_model <- subset(data, select = -c(citta))

# Modello completo
model_full <- lm(prezzo ~ ., data = data_model)
summary(model_full)
## 
## Call:
## lm(formula = prezzo ~ ., data = data_model)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -1235.7  -212.6   -48.0   162.4  1734.6 
## 
## Coefficients:
##                               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)                  364.16129   13.98712  26.035  < 2e-16 ***
## regionecentro                 -0.68082    3.34487  -0.204 0.838714    
## regionesud                  -118.45296    5.33997 -22.182  < 2e-16 ***
## regioneisole                -169.79632    8.30915 -20.435  < 2e-16 ***
## posti.auto                  -123.42067    3.10675 -39.727  < 2e-16 ***
## bagni                        118.41539    4.10579  28.841  < 2e-16 ***
## stanze                       -22.69196    2.16945 -10.460  < 2e-16 ***
## ultimo.piano                 -22.24658    3.91317  -5.685 1.31e-08 ***
## stato.L                       54.38225   16.37820   3.320 0.000899 ***
## stato.Q                       51.30056   12.24728   4.189 2.81e-05 ***
## stato.C                      -26.86050    5.75614  -4.666 3.07e-06 ***
## classe.energetica.L           29.33498    4.24242   6.915 4.74e-12 ***
## classe.energetica.Q          -42.30008    3.90270 -10.839  < 2e-16 ***
## classe.energetica.C          -54.78803    4.32068 -12.680  < 2e-16 ***
## classe.energetica^4          -10.88207    4.49292  -2.422 0.015437 *  
## classe.energetica^5           89.73257    4.65406  19.280  < 2e-16 ***
## classe.energetica^6           72.70311    4.45930  16.304  < 2e-16 ***
## vista.mare                    79.10703   12.20423   6.482 9.14e-11 ***
## riscaldamento.centralizzato  127.46390    3.30510  38.566  < 2e-16 ***
## superficie                     5.20437    0.08191  63.536  < 2e-16 ***
## arredato                      40.71869    3.39514  11.993  < 2e-16 ***
## balcone                      -66.94142    2.95130 -22.682  < 2e-16 ***
## esposizione.esterna          -14.56533    2.92248  -4.984 6.25e-07 ***
## fibra.ottica                  69.66332    3.11214  22.384  < 2e-16 ***
## cancello.elettrico            -1.92756    3.37374  -0.571 0.567770    
## cantina                      -69.02161    3.64482 -18.937  < 2e-16 ***
## giardino.comune               28.53782    3.62639   7.869 3.63e-15 ***
## giardino.privato             -15.47130    5.57289  -2.776 0.005502 ** 
## piscina                      175.86380   14.35456  12.251  < 2e-16 ***
## villa                         41.83015   13.27097   3.152 0.001622 ** 
## appartamento                  34.39576   11.02808   3.119 0.001816 ** 
## attico                       148.63857   16.34594   9.093  < 2e-16 ***
## loft                         116.67513   26.31147   4.434 9.25e-06 ***
## mansarda                     -38.13319   17.62963  -2.163 0.030544 *  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 306.5 on 51679 degrees of freedom
##   (39 osservazioni eliminate a causa di valori mancanti)
## Multiple R-squared:  0.2812, Adjusted R-squared:  0.2807 
## F-statistic: 612.6 on 33 and 51679 DF,  p-value: < 2.2e-16

Commento dei risultati:

Questo modello completo considera tutti i predittori disponibili (escludendo città) per spiegare il prezzo degli immobili. L’output del sommario fornisce i coefficienti di ciascuna variabile, la loro significatività e statistiche generali sull’adattamento del modello, come l’R². Un valore di R² aggiustato più alto rispetto ai modelli più semplici indicherebbe che l’inclusione di più variabili migliora la capacità del modello di spiegare la variazione del prezzo. La significatività dei singoli coefficienti rivela quali fattori hanno un impatto statisticamente significativo sui prezzi degli immobili.

# Grafici diagnostici per il modello completo
par(mfrow = c(2, 2))
plot(model_full, which = 1:4)

Il grafico Residuals vs Fitted mostra una leggera forma a imbuto, suggerendo la presenza di eteroscedasticità (varianza non costante degli errori). Il Q-Q Plot evidenzia alcune deviazioni dalla normalità, in particolare alle estremità della distribuzione. Anche il grafico Scale-Location suggerisce eteroscedasticità, mostrando una tendenza non orizzontale. Il grafico della Cook’s Distance rivela la presenza di alcuni punti con un’influenza relativamente elevata sul modello.

5.3.1. Analisi degli Outlier e Leva

# Calcolo dei residui studentizzati
st.res <- studres(model_full)
pred <- predict(model_full)

# Grafico residui vs valori previsti
p1 <- ggplot(data.frame(pred = pred, st.res = st.res), aes(x = pred, y = st.res)) +
  geom_point(alpha = 0.5) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "red") +
  labs(title = "Residui Studentizzati vs Valori Previsti",
       x = "Valori Previsti",
       y = "Residui Studentizzati") +
  theme_minimal()

# Calcolo delle statistiche di leva
leverage <- hatvalues(model_full)

# Grafico leva vs residui studentizzati
p2 <- ggplot(data.frame(leverage = leverage, st.res = st.res), aes(x = leverage, y = st.res)) +
  geom_point(alpha = 0.5) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "red") +
  labs(title = "Residui Studentizzati vs Leva",
       x = "Leva",
       y = "Residui Studentizzati") +
  theme_minimal()

# Visualizzazione affiancata
gridExtra::grid.arrange(p1, p2, ncol = 2)

I grafici mostrano una chiara eteroscedasticità: i residui aumentano di dispersione con i valori previsti (grafico a sinistra), indicando varianza non costante degli errori. Il grafico dei residui vs leva (a destra) evidenzia alcune osservazioni con leva più alta, ma senza residui estremi, quindi senza gravi outlier influenti.

5.3.2. Analisi di Multicollinearità

# Selezione delle variabili numeriche
Xmat <- subset(data_model, select = -c(regione, prezzo, stato, classe.energetica))

# Matrice di correlazione
corrplot(cor(Xmat), 
         method = "circle", 
         type = "upper", 
         diag = FALSE,
         tl.col = "black",
         tl.srt = 45,
         addCoef.col = "black",
         number.cex = 0.7)

# Approccio alternativo per VIF che evita l'errore
# Inizializziamo un vettore vuoto per i VIF
vif_manual <- numeric(length = ncol(Xmat))
names(vif_manual) <- colnames(Xmat)

# Calcolo manuale dei VIF
for (i in 1:ncol(Xmat)) {
  # Creiamo una formula per ogni variabile come dipendente dalle altre
  vars <- colnames(Xmat)[-i]
  if (length(vars) > 0) {
    formula_str <- paste(colnames(Xmat)[i], "~", paste(vars, collapse = " + "))
    model_i <- lm(formula_str, data = Xmat)
    vif_manual[i] <- 1/(1 - summary(model_i)$r.squared)
  } else {
    vif_manual[i] <- 1
  }
}

# Calcolo della tolleranza
tolerance_manual <- 1/vif_manual

# Creazione del data frame per la visualizzazione
vif_data <- data.frame(
  Variabile = names(vif_manual),
  VIF = vif_manual,
  Tolleranza = tolerance_manual
)

# Visualizzazione della tabella
knitr::kable(vif_data, digits = 3)
Variabile VIF Tolleranza
posti.auto posti.auto 1.210 0.826
bagni bagni 1.659 0.603
stanze stanze 2.641 0.379
ultimo.piano ultimo.piano 1.085 0.922
vista.mare vista.mare 1.012 0.988
riscaldamento.centralizzato riscaldamento.centralizzato 1.113 0.899
superficie superficie 3.330 0.300
arredato arredato 1.137 0.879
balcone balcone 1.156 0.865
esposizione.esterna esposizione.esterna 1.067 0.937
fibra.ottica fibra.ottica 1.261 0.793
cancello.elettrico cancello.elettrico 1.335 0.749
cantina cantina 1.112 0.899
giardino.comune giardino.comune 1.153 0.867
giardino.privato giardino.privato 1.258 0.795
piscina piscina 1.022 0.979
villa villa 1.891 0.529
appartamento appartamento 3.455 0.289
attico attico 1.871 0.534
loft loft 1.211 0.826
mansarda mansarda 1.675 0.597
# Gestione più sicura per identificare le correlazioni elevate
cat("\nVariabili con correlazioni elevate (|r| > 0.7):\n")
## 
## Variabili con correlazioni elevate (|r| > 0.7):
cor_matrix <- cor(Xmat)

# Creiamo una versione migliore che non fallisca
# Troviamo tutte le correlazioni maggiori di 0.7 in valore assoluto
high_correlations <- which(abs(cor_matrix) > 0.7 & abs(cor_matrix) < 1, arr.ind = TRUE)

# Controlliamo se abbiamo trovato correlazioni elevate
if (length(high_correlations) > 0 && is.matrix(high_correlations) && nrow(high_correlations) > 0) {
  # Eliminiamo i duplicati (la correlazione di A con B è uguale a B con A)
  high_correlations <- high_correlations[high_correlations[,1] < high_correlations[,2], , drop = FALSE]
  
  # Controlliamo di nuovo dopo aver rimosso i duplicati
  if (nrow(high_correlations) > 0) {
    cor_data <- data.frame(
      Variabile1 = colnames(Xmat)[high_correlations[,1]],
      Variabile2 = colnames(Xmat)[high_correlations[,2]],
      Correlazione = sapply(1:nrow(high_correlations), function(i) {
        cor_matrix[high_correlations[i,1], high_correlations[i,2]]
      })
    )
    
    print(knitr::kable(cor_data[order(abs(cor_data$Correlazione), decreasing = TRUE), ], digits = 3))
  } else {
    cat("Nessuna correlazione elevata rilevata dopo la rimozione dei duplicati.\n")
  }
} else {
  cat("Nessuna correlazione elevata (|r| > 0.7) rilevata tra le variabili.\n")
}
## 
## 
## |Variabile1 |Variabile2 | Correlazione|
## |:----------|:----------|------------:|
## |stanze     |superficie |        0.782|

Vi è una forte correlazione tra le variabili stanze, bagni e superficie.

5.4. Modello Migliorato con Nuove Feature

# Verifichiamo prima che le colonne esistano
if ("bagni" %in% names(data_model) && "stanze" %in% names(data_model)) {
  # Creazione di una nuova variabile: rapporto bagni/stanze usando dplyr
  data_improved <- data_model %>%
    dplyr::mutate(bagniPerStanze = bagni / stanze) %>%
    dplyr::select(-c(bagni, stanze))  # Rimozione delle variabili originali
} else {
  # Se le colonne non esistono, avvisiamo e creiamo una copia senza modifiche
  cat("Attenzione: Le colonne 'bagni' e/o 'stanze' non esistono nel dataset.\n")
  # Controlliamo quali colonne esistono
  cat("Colonne disponibili:", paste(names(data_model), collapse=", "), "\n\n")
  
  # Creiamo una copia senza modifiche
  data_improved <- data_model
}

# Modello migliorato
model_improved <- lm(prezzo ~ ., data = data_improved)
summary(model_improved)
## 
## Call:
## lm(formula = prezzo ~ ., data = data_improved)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -1236.8  -213.7   -48.9   163.2  1734.4 
## 
## Coefficients:
##                               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)                  350.45611   14.74985  23.760  < 2e-16 ***
## regionecentro                  3.90523    3.35818   1.163 0.244875    
## regionesud                  -110.88493    5.36661 -20.662  < 2e-16 ***
## regioneisole                -165.14998    8.36193 -19.750  < 2e-16 ***
## posti.auto                  -120.92390    3.12365 -38.712  < 2e-16 ***
## ultimo.piano                 -22.68813    3.93884  -5.760 8.45e-09 ***
## stato.L                       64.40546   16.48113   3.908 9.32e-05 ***
## stato.Q                       49.08030   12.32705   3.982 6.86e-05 ***
## stato.C                      -25.57936    5.79362  -4.415 1.01e-05 ***
## classe.energetica.L           29.76657    4.26997   6.971 3.18e-12 ***
## classe.energetica.Q          -44.57160    3.92710 -11.350  < 2e-16 ***
## classe.energetica.C          -53.57780    4.34927 -12.319  < 2e-16 ***
## classe.energetica^4          -10.49376    4.52231  -2.320 0.020321 *  
## classe.energetica^5           90.16571    4.68467  19.247  < 2e-16 ***
## classe.energetica^6           72.46087    4.48799  16.146  < 2e-16 ***
## vista.mare                    76.60712   12.28321   6.237 4.50e-10 ***
## riscaldamento.centralizzato  126.30051    3.31898  38.054  < 2e-16 ***
## superficie                     5.86227    0.05380 108.955  < 2e-16 ***
## arredato                      38.21767    3.41543  11.190  < 2e-16 ***
## balcone                      -67.77538    2.97235 -22.802  < 2e-16 ***
## esposizione.esterna          -15.39456    2.94116  -5.234 1.66e-07 ***
## fibra.ottica                  68.56524    3.13171  21.894  < 2e-16 ***
## cancello.elettrico             0.04459    3.39474   0.013 0.989520    
## cantina                      -69.36742    3.66849 -18.909  < 2e-16 ***
## giardino.comune               28.88227    3.65022   7.912 2.57e-15 ***
## giardino.privato             -14.00573    5.60857  -2.497 0.012521 *  
## piscina                      180.39012   14.44640  12.487  < 2e-16 ***
## villa                         49.25911   13.35399   3.689 0.000226 ***
## appartamento                  28.08135   11.10320   2.529 0.011438 *  
## attico                       149.86378   16.45160   9.109  < 2e-16 ***
## loft                         109.99624   26.47347   4.155 3.26e-05 ***
## mansarda                     -45.34171   17.74434  -2.555 0.010613 *  
## bagniPerStanze               103.49688    6.81792  15.180  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 308.5 on 51680 degrees of freedom
##   (39 osservazioni eliminate a causa di valori mancanti)
## Multiple R-squared:  0.2717, Adjusted R-squared:  0.2712 
## F-statistic: 602.5 on 32 and 51680 DF,  p-value: < 2.2e-16

Il modello di regressione spiega circa il 27% della variabilità del prezzo degli immobili. Le variabili più significative includono la superficie (con un forte impatto positivo sul prezzo), la vista mare, la piscina, e la classe energetica. Le case nelle regioni del sud e nelle isole tendono ad avere prezzi più bassi. Alcune variabili come posti auto e giardino privato mostrano effetti meno intuitivi. Il modello, pur significativo, lascia spazio a miglioramenti, come l’inclusione di variabili aggiuntive.

# Grafici diagnostici per il modello migliorato
par(mfrow = c(2, 2))
plot(model_improved, which = 1:4)

par(mfrow = c(1, 1))

Il grafico dei residui rispetto ai valori previsti mostra una forma curva e una dispersione crescente, suggerendo la presenza di non linearità ed eteroscedasticità. Il Q-Q plot evidenzia deviazioni dai quantili teorici, soprattutto nelle code, indicando che i residui non seguono una distribuzione normale. Il grafico Scale-Location conferma l’eteroscedasticità con una tendenza crescente nella variabilità dei residui standardizzati. Il grafico delle Cook’s distance mostra alcune osservazioni potenzialmente influenti ma nessuna che sembri dominare il modello.

5.4.1. Analisi di Multicollinearità per il Modello Migliorato

# Selezione delle variabili numeriche
Xmat_improved <- subset(data_improved, select = -c(regione, prezzo, stato, classe.energetica))

# Matrice di correlazione
corrplot(cor(Xmat_improved), 
         method = "circle", 
         type = "upper", 
         diag = FALSE,
         tl.col = "black",
         tl.srt = 45,
         addCoef.col = "black",
         number.cex = 0.7)

# Calcolo manuale dei VIF
vif_manual_improved <- numeric(length = ncol(Xmat_improved))
names(vif_manual_improved) <- colnames(Xmat_improved)

for (i in 1:ncol(Xmat_improved)) {
  vars <- colnames(Xmat_improved)[-i]
  if (length(vars) > 0) {
    formula_str <- paste(colnames(Xmat_improved)[i], "~", paste(vars, collapse = " + "))
    model_i <- lm(formula_str, data = Xmat_improved)
    vif_manual_improved[i] <- 1 / (1 - summary(model_i)$r.squared)
  } else {
    vif_manual_improved[i] <- 1
  }
}

# Calcolo della tolleranza
tolerance_manual_improved <- 1 / vif_manual_improved

# Creazione del data frame per la visualizzazione
vif_data_improved <- data.frame(
  Variabile = names(vif_manual_improved),
  VIF = vif_manual_improved,
  Tolleranza = tolerance_manual_improved
)

# Visualizzazione della tabella
knitr::kable(vif_data_improved, digits = 3)
Variabile VIF Tolleranza
posti.auto posti.auto 1.208 0.828
ultimo.piano ultimo.piano 1.084 0.922
vista.mare vista.mare 1.012 0.988
riscaldamento.centralizzato riscaldamento.centralizzato 1.106 0.904
superficie superficie 1.408 0.710
arredato arredato 1.136 0.880
balcone balcone 1.157 0.864
esposizione.esterna esposizione.esterna 1.067 0.937
fibra.ottica fibra.ottica 1.261 0.793
cancello.elettrico cancello.elettrico 1.334 0.750
cantina cantina 1.110 0.901
giardino.comune giardino.comune 1.153 0.867
giardino.privato giardino.privato 1.258 0.795
piscina piscina 1.022 0.979
villa villa 1.890 0.529
appartamento appartamento 3.456 0.289
attico attico 1.871 0.535
loft loft 1.210 0.826
mansarda mansarda 1.674 0.597
bagniPerStanze bagniPerStanze 1.175 0.851

I valori VIF sono tutti inferiori a 5, indicando un’assenza di multicollinearità problematica tra le variabili. Tuttavia, la variabile appartamento presenta il VIF più alto (3.456) e una tolleranza più bassa (0.289), suggerendo una moderata correlazione con altre variabili che potrebbe essere monitorata. Nel complesso, il modello non mostra segnali critici di collinearità. Vuoi che evidenzi anche le soglie standard per un’analisi più approfondita?

6. Selezione delle Variabili

# Selezione di sottoinsiemi di variabili
regfit.full <- regsubsets(prezzo ~ ., 
                         data = data_improved, 
                         nvmax = min(33, ncol(data_improved) - 1), 
                         method = "exhaustive")

# Riepilogo dei risultati
reg_summary <- summary(regfit.full)
# Grafici per valutare i modelli
par(mfrow = c(2, 2))

# Grafico RSS
plot(reg_summary$rss, xlab = "Numero di Predittori", 
     ylab = "RSS", type = "b", 
     col = "red", pch = 19,
     main = "Residual Sum of Squares")

# Grafico R2
plot(reg_summary$rsq, xlab = "Numero di Predittori", 
     ylab = "R²", type = "b", 
     col = "red", pch = 19,
     main = "R²")

# Grafico R2 aggiustato
AdjR2 <- reg_summary$adjr2
Best <- which.max(AdjR2)
plot(AdjR2, xlab = "Numero di Predittori", 
     ylab = "R² Aggiustato", type = "b", 
     col = "red", pch = 19,
     main = "R² Aggiustato")
points(Best, AdjR2[Best], col = "blue", cex = 2, pch = 20)

# Grafico BIC
plot(reg_summary$bic, xlab = "Numero di Predittori", 
     ylab = "BIC", type = "b", 
     col = "red", pch = 19,
     main = "BIC")
points(which.min(reg_summary$bic), min(reg_summary$bic), 
       col = "blue", cex = 2, pch = 20)

par(mfrow = c(1, 1))
# Visualizzazione delle variabili selezionate
par(mar = c(4, 10, 4, 2))  # Aumentare il margine sinistro
plot(regfit.full, scale = "adjr2")

# Coefficienti del miglior modello secondo R² aggiustato
best_coef <- coef(regfit.full, Best)
best_coef_df <- data.frame(
  Variabile = names(best_coef),
  Coefficiente = best_coef
)
knitr::kable(best_coef_df, digits = 3)
Variabile Coefficiente
(Intercept) (Intercept) 353.901
regionesud regionesud -111.223
regioneisole regioneisole -165.080
posti.auto posti.auto -122.870
ultimo.piano ultimo.piano -22.419
stato.L stato.L 133.181
classe.energetica.L classe.energetica.L 29.949
classe.energetica.Q classe.energetica.Q -45.380
classe.energetica.C classe.energetica.C -51.949
classe.energetica^5 classe.energetica^5 92.205
classe.energetica^6 classe.energetica^6 72.993
vista.mare vista.mare 76.728
riscaldamento.centralizzato riscaldamento.centralizzato 126.379
superficie superficie 5.864
arredato arredato 38.158
balcone balcone -66.960
esposizione.esterna esposizione.esterna -15.016
fibra.ottica fibra.ottica 69.244
cantina cantina -69.879
giardino.comune giardino.comune 30.507
piscina piscina 179.921
attico attico 122.755
mansarda mansarda -73.903
bagniPerStanze bagniPerStanze 104.100

6.1. Modello Finale (Semplificato)

data_final <- subset(data_improved, select = -cancello.elettrico)
model_final <- lm(prezzo ~ ., data = data_final)
summary(model_final)
## 
## Call:
## lm(formula = prezzo ~ ., data = data_final)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -1236.8  -213.7   -48.9   163.2  1734.4 
## 
## Coefficients:
##                              Estimate Std. Error t value Pr(>|t|)    
## (Intercept)                  350.4518    14.7461  23.766  < 2e-16 ***
## regionecentro                  3.9019     3.3486   1.165 0.243929    
## regionesud                  -110.8834     5.3653 -20.667  < 2e-16 ***
## regioneisole                -165.1531     8.3585 -19.759  < 2e-16 ***
## posti.auto                  -120.9172     3.0815 -39.240  < 2e-16 ***
## ultimo.piano                 -22.6892     3.9379  -5.762 8.37e-09 ***
## stato.L                       64.4082    16.4796   3.908 9.30e-05 ***
## stato.Q                       49.0800    12.3269   3.982 6.86e-05 ***
## stato.C                      -25.5790     5.7935  -4.415 1.01e-05 ***
## classe.energetica.L           29.7694     4.2643   6.981 2.96e-12 ***
## classe.energetica.Q          -44.5681     3.9182 -11.375  < 2e-16 ***
## classe.energetica.C          -53.5771     4.3489 -12.320  < 2e-16 ***
## classe.energetica^4          -10.4958     4.5196  -2.322 0.020220 *  
## classe.energetica^5           90.1601     4.6651  19.326  < 2e-16 ***
## classe.energetica^6           72.4572     4.4791  16.177  < 2e-16 ***
## vista.mare                    76.6057    12.2827   6.237 4.50e-10 ***
## riscaldamento.centralizzato  126.2997     3.3183  38.061  < 2e-16 ***
## superficie                     5.8623     0.0538 108.957  < 2e-16 ***
## arredato                      38.2176     3.4154  11.190  < 2e-16 ***
## balcone                      -67.7721     2.9618 -22.882  < 2e-16 ***
## esposizione.esterna          -15.3920     2.9347  -5.245 1.57e-07 ***
## fibra.ottica                  68.5796     2.9339  23.375  < 2e-16 ***
## cantina                      -69.3624     3.6482 -19.013  < 2e-16 ***
## giardino.comune               28.8896     3.6067   8.010 1.17e-15 ***
## giardino.privato             -14.0007     5.5955  -2.502 0.012348 *  
## piscina                      180.3957    14.4401  12.493  < 2e-16 ***
## villa                         49.2649    13.3466   3.691 0.000223 ***
## appartamento                  28.0853    11.0990   2.530 0.011395 *  
## attico                       149.8708    16.4427   9.115  < 2e-16 ***
## loft                         110.0022    26.4693   4.156 3.25e-05 ***
## mansarda                     -45.3339    17.7341  -2.556 0.010582 *  
## bagniPerStanze               103.4988     6.8163  15.184  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 308.5 on 51681 degrees of freedom
##   (39 osservazioni eliminate a causa di valori mancanti)
## Multiple R-squared:  0.2717, Adjusted R-squared:  0.2713 
## F-statistic: 621.9 on 31 and 51681 DF,  p-value: < 2.2e-16
# Grafici diagnostici per il modello finale
par(mfrow = c(2, 2))
plot(model_final, which = 1:4)

par(mfrow = c(1, 1))

Proviamo a fare un modello con le migliori 5 variabili:

data_final_simplified <- subset(data_improved, select = c("prezzo", "superficie", "posti.auto", "bagniPerStanze", "riscaldamento.centralizzato", "regione"))
model_final_simplified <- lm(prezzo ~ ., data = data_final_simplified)
summary(model_final_simplified)
## 
## Call:
## lm(formula = prezzo ~ ., data = data_final_simplified)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1200.30  -223.84   -57.99   164.19  1983.44 
## 
## Coefficients:
##                               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)                  371.54943    6.33386  58.661  < 2e-16 ***
## superficie                     5.46368    0.05191 105.260  < 2e-16 ***
## posti.auto                  -119.61232    3.04805 -39.242  < 2e-16 ***
## bagniPerStanze               158.22153    6.99235  22.628  < 2e-16 ***
## riscaldamento.centralizzato   91.27755    3.32250  27.473  < 2e-16 ***
## regionecentro                 10.42445    3.29058   3.168  0.00154 ** 
## regionesud                  -143.40124    5.30299 -27.042  < 2e-16 ***
## regioneisole                -180.91656    8.52768 -21.215  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 321.3 on 51705 degrees of freedom
##   (39 osservazioni eliminate a causa di valori mancanti)
## Multiple R-squared:  0.2098, Adjusted R-squared:  0.2097 
## F-statistic:  1961 on 7 and 51705 DF,  p-value: < 2.2e-16

7. Conclusioni

In questa analisi abbiamo esplorato vari modelli per comprendere i fattori che influenzano i prezzi degli immobili in Italia. I principali risultati includono:

  1. La superficie è il fattore che maggiormente influenza il prezzo degli immobili.
  2. Esiste una significativa differenza nei prezzi tra le diverse aree geografiche.
  3. Le caratteristiche come lo stato dell’immobile e la classe energetica hanno un impatto significativo sul prezzo.
  4. Il modello di regressione ottimale include un sottoinsieme di variabili selezionate che bilanciano complessità e capacità predittiva.
  5. L’analisi discriminante quadratica può essere utilizzata per classificare gli immobili in base alla loro posizione geografica con una discreta accuratezza.